// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "Core/IOS/USB/USB_KBD.h"

#include <cstring>
#include <queue>

#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Common/Logging/Log.h"
#include "Common/Swap.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"  // Local core functions
#include "Core/HW/Memmap.h"
#include "InputCommon/ControlReference/ControlReference.h"  // For background input check

#ifdef _WIN32
#include <windows.h>
#endif

namespace IOS::HLE::Device
{
USB_KBD::SMessageData::SMessageData(u32 type, u8 modifiers, u8* pressed_keys)
{
  MsgType = Common::swap32(type);
  Unk1 = 0;  // swapped
  Modifiers = modifiers;
  Unk2 = 0;

  if (pressed_keys)  // Doesn't need to be in a specific order
    memcpy(PressedKeys, pressed_keys, sizeof(PressedKeys));
  else
    memset(PressedKeys, 0, sizeof(PressedKeys));
}

// TODO: support in netplay/movies.

USB_KBD::USB_KBD(Kernel& ios, const std::string& device_name) : Device(ios, device_name)
{
}

IPCCommandResult USB_KBD::Open(const OpenRequest& request)
{
  INFO_LOG(IOS, "USB_KBD: Open");
  IniFile ini;
  ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX));
  ini.GetOrCreateSection("USB Keyboard")->Get("Layout", &m_KeyboardLayout, KBD_LAYOUT_QWERTY);

  m_MessageQueue = std::queue<SMessageData>();
  for (bool& pressed : m_OldKeyBuffer)
  {
    pressed = false;
  }

  m_OldModifiers = 0x00;

  // m_MessageQueue.push(SMessageData(MSG_KBD_CONNECT, 0, nullptr));
  return Device::Open(request);
}

IPCCommandResult USB_KBD::Write(const ReadWriteRequest& request)
{
  // Stubbed.
  return GetDefaultReply(IPC_SUCCESS);
}

IPCCommandResult USB_KBD::IOCtl(const IOCtlRequest& request)
{
  if (SConfig::GetInstance().m_WiiKeyboard && !Core::WantsDeterminism() &&
      ControlReference::InputGateOn() && !m_MessageQueue.empty())
  {
    Memory::CopyToEmu(request.buffer_out, &m_MessageQueue.front(), sizeof(SMessageData));
    m_MessageQueue.pop();
  }
  return GetDefaultReply(IPC_SUCCESS);
}

bool USB_KBD::IsKeyPressed(int _Key)
{
#ifdef _WIN32
  if (GetAsyncKeyState(_Key) & 0x8000)
    return true;
  else
    return false;
#else
  // TODO: do it for non-Windows platforms
  return false;
#endif
}

void USB_KBD::Update()
{
  if (!SConfig::GetInstance().m_WiiKeyboard || Core::WantsDeterminism() || !m_is_active)
    return;

  u8 Modifiers = 0x00;
  u8 PressedKeys[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  bool GotEvent = false;
  int num_pressed_keys = 0;
  for (int i = 0; i < 256; i++)
  {
    bool KeyPressedNow = IsKeyPressed(i);
    bool KeyPressedBefore = m_OldKeyBuffer[i];
    u8 KeyCode = 0;

    if (KeyPressedNow ^ KeyPressedBefore)
    {
      if (KeyPressedNow)
      {
        switch (m_KeyboardLayout)
        {
        case KBD_LAYOUT_QWERTY:
          KeyCode = m_KeyCodesQWERTY[i];
          break;

        case KBD_LAYOUT_AZERTY:
          KeyCode = m_KeyCodesAZERTY[i];
          break;
        }

        if (KeyCode == 0x00)
          continue;

        PressedKeys[num_pressed_keys] = KeyCode;

        num_pressed_keys++;
        if (num_pressed_keys == 6)
          break;
      }

      GotEvent = true;
    }

    m_OldKeyBuffer[i] = KeyPressedNow;
  }

#ifdef _WIN32
  if (GetAsyncKeyState(VK_LCONTROL) & 0x8000)
    Modifiers |= 0x01;
  if (GetAsyncKeyState(VK_LSHIFT) & 0x8000)
    Modifiers |= 0x02;
  if (GetAsyncKeyState(VK_MENU) & 0x8000)
    Modifiers |= 0x04;
  if (GetAsyncKeyState(VK_LWIN) & 0x8000)
    Modifiers |= 0x08;
  if (GetAsyncKeyState(VK_RCONTROL) & 0x8000)
    Modifiers |= 0x10;
  if (GetAsyncKeyState(VK_RSHIFT) & 0x8000)
    Modifiers |= 0x20;
  if (GetAsyncKeyState(VK_MENU) &
      0x8000)  // TODO: VK_MENU is for ALT, not for ALT GR (ALT GR seems to work though...)
    Modifiers |= 0x40;
  if (GetAsyncKeyState(VK_RWIN) & 0x8000)
    Modifiers |= 0x80;
#else
// TODO: modifiers for non-Windows platforms
#endif

  if (Modifiers ^ m_OldModifiers)
  {
    GotEvent = true;
    m_OldModifiers = Modifiers;
  }

  if (GotEvent)
    m_MessageQueue.push(SMessageData(MSG_EVENT, Modifiers, PressedKeys));
}

// Crazy ugly
#ifdef _WIN32
u8 USB_KBD::m_KeyCodesQWERTY[256] = {

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x2A,  // Backspace
    0x2B,  // Tab
    0x00, 0x00,
    0x00,  // Clear
    0x28,  // Return
    0x00, 0x00,
    0x00,  // Shift
    0x00,  // Control
    0x00,  // ALT
    0x48,  // Pause
    0x39,  // Capital
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x29,  // Escape
    0x00, 0x00, 0x00, 0x00,
    0x2C,  // Space
    0x4B,  // Prior
    0x4E,  // Next
    0x4D,  // End
    0x4A,  // Home
    0x50,  // Left
    0x52,  // Up
    0x4F,  // Right
    0x51,  // Down
    0x00, 0x00, 0x00,
    0x46,  // Print screen
    0x49,  // Insert
    0x4C,  // Delete
    0x00,
    // 0 -> 9
    0x27, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00,
    // A -> Z
    0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
    0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00,
    // Numpad 0 -> 9
    0x62, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61,
    0x55,  // Multiply
    0x57,  // Add
    0x00,  // Separator
    0x56,  // Subtract
    0x63,  // Decimal
    0x54,  // Divide
    // F1 -> F12
    0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
    // F13 -> F24
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x53,  // Numlock
    0x47,  // Scroll lock
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    // Modifier keys
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x33,  // ';'
    0x2E,  // Plus
    0x36,  // Comma
    0x2D,  // Minus
    0x37,  // Period
    0x38,  // '/'
    0x35,  // '~'
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x2F,  // '['
    0x32,  // '\'
    0x30,  // ']'
    0x34,  // '''
    0x00,  //
    0x00,  // Nothing interesting past this point.

};

u8 USB_KBD::m_KeyCodesAZERTY[256] = {

    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x2A,  // Backspace
    0x2B,  // Tab
    0x00, 0x00,
    0x00,  // Clear
    0x28,  // Return
    0x00, 0x00,
    0x00,  // Shift
    0x00,  // Control
    0x00,  // ALT
    0x48,  // Pause
    0x39,  // Capital
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x29,  // Escape
    0x00, 0x00, 0x00, 0x00,
    0x2C,  // Space
    0x4B,  // Prior
    0x4E,  // Next
    0x4D,  // End
    0x4A,  // Home
    0x50,  // Left
    0x52,  // Up
    0x4F,  // Right
    0x51,  // Down
    0x00, 0x00, 0x00,
    0x46,  // Print screen
    0x49,  // Insert
    0x4C,  // Delete
    0x00,
    // 0 -> 9
    0x27, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00,
    // A -> Z
    0x14, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x33, 0x11, 0x12, 0x13,
    0x04, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1D, 0x1B, 0x1C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00,
    // Numpad 0 -> 9
    0x62, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61,
    0x55,  // Multiply
    0x57,  // Add
    0x00,  // Separator
    0x56,  // Substract
    0x63,  // Decimal
    0x54,  // Divide
    // F1 -> F12
    0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
    // F13 -> F24
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x53,  // Numlock
    0x47,  // Scroll lock
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    // Modifier keys
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x30,  // '$'
    0x2E,  // Plus
    0x10,  // Comma
    0x00,  // Minus
    0x36,  // Period
    0x37,  // '/'
    0x34,  // ' '
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x2D,  // ')'
    0x32,  // '\'
    0x2F,  // '^'
    0x00,  // ' '
    0x38,  // '!'
    0x00,  // Nothing interesting past this point.

};
#else
u8 USB_KBD::m_KeyCodesQWERTY[256] = {0};

u8 USB_KBD::m_KeyCodesAZERTY[256] = {0};
#endif
}  // namespace IOS::HLE::Device
